package application;

import java.sql.Statement;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import vo.User;
import JDBC.DatabaseDao;
import Jama.Matrix;
import Utils.StringUtils;
import dao.InlinkDao;
import dao.OutlinkDao;
import dao.UserDao;
import dao.UserRelationDao;

/*

 * Developed by Nima Goodarzi

 * Website: http://www.javadev.org

 * Email: nima@javadev.org

 */

public class PageRank {

	private final double DAMPING_FACTOR = 0.85;
	public static boolean isTest = false;
	private List params = new ArrayList();

	public static void insertInlinks() {
		ArrayList<User> users = UserDao.getAll();
		for (int i = 0; i < users.size(); i++) {
			String userId = users.get(i).getUserId();
			String[] followers = UserRelationDao.getFollowersByUserId(userId);
			String inlinks = "";
			for (int j = 0; j < followers.length; j++)
				inlinks += followers[j] + "#";
			if (inlinks.length() >= 1)
				inlinks = inlinks.substring(0, inlinks.length() - 1);// 去掉最后一个#
			try {
				String sql = "";
				if (inlinks != null && !inlinks.equals(""))
					sql = "insert into inlinks values(" + userId + ",'" + inlinks + "')";
				System.out.println(sql);
				Statement stmt = DatabaseDao.getConnection().createStatement();
				stmt.executeUpdate(sql);
			} catch (Exception e) {
				e.printStackTrace();
			}

		}

	}

	public static void insertOutlinks() {
		ArrayList<User> users = UserDao.getAll();
		for (int i = 0; i < users.size(); i++) {
			String userId = users.get(i).getUserId();
			String[] friends = UserRelationDao.getFriendsByUserId(userId);
			String outlinks = "";
			for (int j = 0; j < friends.length; j++)
				outlinks += friends[j] + "#";
			if (outlinks.length() >= 1)
				outlinks = outlinks.substring(0, outlinks.length() - 1);// 去掉最后一个#
			try {
				String sql = "";
				if (outlinks != null && !outlinks.equals(""))
					sql = "insert into outlinks values(" + userId + ",'" + outlinks + "')";
				System.out.println(sql);
				Statement stmt = DatabaseDao.getConnection().createStatement();
				stmt.executeUpdate(sql);
			} catch (Exception e) {
				e.printStackTrace();
			}

		}

	}

	public static void initSocialGraph() {
		insertInlinks();
		insertOutlinks();
	}


	/*
	 * 
	 * Solve the equation of ax=b, which : a is the generated matrix based on
	 * 
	 * the parameter constants. x is the page ranks matrix. b is a n*1 matrix
	 * 
	 * which all the values are equal to the damping factor.
	 */

	public double rank(String pageId) {

		generateParamList(pageId);

		Matrix a = new Matrix(generateMatrix());

		double[][] arrB = new double[params.size()][1];

		for (int i = 0; i < params.size(); i++) {

			arrB[i][0] = 1 - DAMPING_FACTOR;

		}

		Matrix b = new Matrix(arrB);

		// Solve the equation and get the page ranks

		Matrix x = a.solve(b);

		int ind = 0;

		int cnt = 0;

		for (Iterator it = params.iterator(); it.hasNext();) {

			String curPage = (String) it.next();

			if (curPage.equals(pageId))

				ind = cnt;

			cnt++;

		}

		return x.getArray()[ind][0];

	}

	/*
	 * 
	 * This method generates the matrix of the linear equations. The generated
	 * 
	 * matrix is a n*n matrix where n is number of the related pages.
	 */

	private double[][] generateMatrix() {

		double[][] arr = new double[params.size()][params.size()];

		for (int i = 0; i < params.size(); i++) {

			for (int j = 0; j < params.size(); j++) {

				arr[i][j] = getMultiFactor((String) params.get(i),

				(String) params.get(j));

			}

		}

		return arr;

	}

	/*
	 * 
	 * This method returns the constant of the given variable in the linear
	 * 
	 * equation.
	 */

	private double getMultiFactor(String sourceId, String linkId) {

		if (sourceId.equals(linkId))

			return 1;

		else {

			// String[] inc = getInboundLinks(sourceId);//TODO:换新浪的social graph
			String[] inc = getInboundLinks_sina(sourceId);// TODO:换新浪的social
															// graph

			if (inc == null)
				return 0;
			for (int i = 0; i < inc.length; i++) {

				if (inc[i].equals(linkId)) {

					// return -1 * (DAMPING_FACTOR /
					// getOutboundLinks(linkId).length);//TODO：换成新浪的social graph
					return -1 * (DAMPING_FACTOR / getOutboundLinks_sina(linkId).length);

				}

			}

		}

		return 0;

	}

	/*
	 * 
	 * This method returns list of the related pages. This list is also the
	 * 
	 * parameters in the linear equation.
	 */

	private void generateParamList(String pageId) {

		// Add the starting page.
		System.out.println("pageId:" + pageId);

		if (!params.contains(pageId))

			params.add(pageId);

		// Get list of the inbound pages

		// String[] inc = getInboundLinks(pageId);//TODO:换新浪的social graph
		String[] inc = getInboundLinks_sina(pageId);// TODO:换新浪的social graph

		// Add the inbound links to the params list and do same for inbound

		// links
		if (inc == null)
			return;

		for (int i = 0; i < inc.length; i++) {

			if (!params.contains(inc[i]))

				generateParamList(inc[i]);

		}

	}

	/*
	 * 
	 * Return list of the inbound links to a given page.
	 */
	/*
	 * 
	 * Return list of the inbound links to a given page.
	 */

	private String[] getInboundLinks_sina(String pageId) {

		// This simulates a simple page collection
		Map map = new HashMap();

		// 使用sina的social graph算
		if (isTest == false) {
			ArrayList<String> followers = UserRelationDao.getAllFollowers();
			ArrayList<String> friends = UserRelationDao.getAllFollowers();
			ArrayList<String> users = StringUtils.merge(followers, friends);
			for (int i = 0; i < users.size(); i++) {
				String userId = users.get(i);
				String[] followersLinks = InlinkDao.getInlinksByUserId(userId);
				map.put(userId, followersLinks);
			}
		}

		// //使用自己构建的测试graph算
		if (isTest == true) {
			String[] users = { "a", "b", "c", "d", "e", "f", "g", "h", "i" };
			for (int i = 0; i < users.length; i++) {
				String userId = users[i];
				String[] followers = InlinkDao.getInlinksByUserId(userId);
				map.put(userId, followers);
			}

		}
		return (String[]) map.get(pageId);

	}

	/*
	 * 
	 * Returns list of the outbound links from a page.
	 */

	private String[] getOutboundLinks_sina(String pageId) {
		// This simulates a simple page collection
		Map<String, String[]> map = new HashMap();

		// 使用新浪的social graph算
		if (isTest == false) {
			ArrayList<String> followers = UserRelationDao.getAllFollowers();
			ArrayList<String> friends = UserRelationDao.getAllFollowers();
			ArrayList<String> users = StringUtils.merge(followers, friends);
			for (int i = 0; i < users.size(); i++) {
				String userId = users.get(i);
				String[] friendsLinks = OutlinkDao.getOutlinksByUserId(userId);
				map.put(userId, friendsLinks);

			}
		}

		// 使用自己构建的social graph算
		if (isTest == true) {
			String[] users = { "a", "b", "c", "d", "e", "f", "g", "h", "i" };
			for (int i = 0; i < users.length; i++) {
				String userId = users[i];
				String[] friends = OutlinkDao.getOutlinksByUserId(userId);
				map.put(userId, friends);

			}

		}
		return (String[]) map.get(pageId);

	}
	public static void main(String[] args) {

		// initSocialGraph();// 计算一次，然后就一直用下去了
		PageRank ranking = new PageRank();

		// 使用测试的social graph计算
		if (isTest == true) {
			System.out.println("a:" + ranking.rank("a"));
			System.out.println("b:" + ranking.rank("b"));
			System.out.println("c:" + ranking.rank("c"));
			System.out.println("d:" + ranking.rank("d"));
		} else {
			System.out.println("我:" + ranking.rank("1130842987"));
			System.out.println("田野:" + ranking.rank("1730273015"));
			System.out.println("小涛:" + ranking.rank("1804052100"));
			System.out.println("于魁飞:" + ranking.rank("1895381311"));
		}

	}

}
